package com.chenfan.magic.tags.handler;

import cn.afterturn.easypoi.entity.ImageEntity;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.chenfan.magic.base.Kv;
import com.chenfan.magic.config.DynamicTableHelper;
import com.chenfan.magic.constant.MagicConstant;
import com.chenfan.magic.constant.RuntimeConstant;
import com.chenfan.magic.core.I;
import com.chenfan.magic.core.IdcEngineEntry;
import com.chenfan.magic.core.M;
import com.chenfan.magic.core.executer.EOFExecuter;
import com.chenfan.magic.entity.*;
import com.chenfan.magic.exception.MagicException;
import com.chenfan.magic.resolver._MainResolver;
import com.chenfan.magic.service.IDatasourceTypeService;
import com.chenfan.magic.service.impl.DataSourcePool;
import com.chenfan.magic.tags.TagTemplate;
import com.chenfan.magic.utils.*;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipOutputStream;

/**
 * template 标签处理器
 *
 * @author  周夕
 * @date 2020-02-26 18:21
 */
@Slf4j
@Component
@AllArgsConstructor
public class TagTemplateHandler {

    private DataSourcePool dataSourcePool;

    private IDatasourceTypeService datasourceTypeService;

    /**
     * 执行入口
     *
     * @param mqlModel
     * @param key
     * @return
     */
    public boolean execute(
            MQLModel mqlModel,
            Map<String, Object> idcRuntimePool,
            String key,
            Map<String, Object> paramMap,
            Map<String, Object> resMap,
            Map<String, Object> eofMap,
            Map<String, Object> resolveMap,
            Map<String, Object> attributeMap,
            Kv kv,
            TTLModel ttlModel
    ) {
        //执行器时间统计
        TTLDetailModel ttlDetailModel = new TTLDetailModel();
        String prefixKey = TagTemplate.tagNames + "#" + key;
        ttlDetailModel.start(prefixKey);
        TagTemplate templateModel = mqlModel.getTemplate(key, idcRuntimePool, paramMap, attributeMap);
        kv.set(RuntimeConstant.RUNTIME_EXECUTE_VAR_KEY_PREFIXKEY, prefixKey);
        kv.set(RuntimeConstant.RUNTIME_EXECUTE_VAR_KEY_RESPOND, templateModel.getRespond());
        kv.set(RuntimeConstant.RUNTIME_EXECUTE_VAR_KEY_RESULTKEY, templateModel.getResultKey());

        Map<String, Object> res = Maps.newHashMap();
        String resolveScript = "";
        //防止引用检测
        String json = JSON.toJSONString(paramMap, SerializerFeature.DisableCircularReferenceDetect);
        Map<String, Object> pm = FastJsonKit.jsonstring2Map(json);
        pm.remove("#params");
        pm.remove("#context");

        Map<String, IdcDatasource> idcDatasourceMap = (Map<String, IdcDatasource>) idcRuntimePool.get(RuntimeConstant.RUNTIME_DATASOURCES);
        IdcDatasource idcDatasource = null;
        if (StringKit.isBlank(templateModel.getDatasource())) {
            //若未配置数据源, 则默认为主数据源
            idcDatasource = idcDatasourceMap.get("master");
        } else {
            idcDatasource = idcDatasourceMap.get(templateModel.getDatasource());
        }
        IdcDatasourceType idcDatasourceType = (IdcDatasourceType) IdcEngineEntry.takeRuntimeVar(idcRuntimePool, datasourceTypeService.get(idcDatasource.getDatasourceTypeId()), RuntimeConstant.RUNTIME_DATASOURCE_TYPE);
        resolveScript = new _MainResolver().resolveScope(idcRuntimePool, ttlModel, prefixKey, templateModel, paramMap);
        resolveMap.put(templateModel.getResultKey(), resolveScript.replaceAll("[\\s\\t\\n\\r]", " "));
        if (!EOFExecuter.run(eofMap, resolveScript + "", prefixKey)) {
            ttlModel.getExecuter().put(ttlDetailModel.end());
            return false;
        }
        res = run(idcRuntimePool, idcDatasource, idcDatasourceType, resolveScript, templateModel);

        res.put("#params", pm);
        res.put("#context", resolveScript);
        resMap.put(prefixKey, res);
        paramMap.putAll(attributeMap);
        paramMap.putAll(res);

        ttlModel.getExecuter().put(ttlDetailModel.end());
        return true;
    }

    /**
     * 大数据导出
     *
     * @param idcRuntimePool
     * @param res
     * @param templateModel
     */
    public boolean export(Map<String, Object> idcRuntimePool, Map<String, Object> res, TagTemplate templateModel) {
        if (templateModel.getExport()) {
            Workbook workbook = null;
            Date start = new Date();
            ExportParams params = new ExportParams("大数据测试", "测试");
            List<Map<String, Object>> data = Lists.newArrayList();
            if ("rows".equalsIgnoreCase(templateModel.getResultType())) {
                data = (List<Map<String, Object>>) res.get(templateModel.getResultKey());
            } else if ("row".equalsIgnoreCase(templateModel.getResultType())) {
                data.add((Map<String, Object>) res.get(templateModel.getResultKey()));
            } else {
                return false;
            }
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ZipOutputStream zip = new ZipOutputStream(outputStream);
            List<ExcelExportEntity> entity = Lists.newArrayList();
            List<Map<String, Object>> rows = Lists.newArrayList();
            List<String> keyList = Lists.newArrayList();
            if (data.size() > 0) {
                keyList = MapKit.getKeys(data.get(0));
            }
            int i = 0;
            String folder = System.getProperty("java.io.tmpdir") + "/idc/export/" + templateModel.getId() + "/" + IDKit.snowFlake();
            for (Map<String, Object> row : data) {
                i++;
                for (String key : keyList) {
                    if (i == 1) {
                        entity.add(new ExcelExportEntity(key, key));
                    }
                    //图片列处理
                    if (StringKit.isNotBlank(templateModel.getExportImages()) && (MagicConstant.COMMA + templateModel.getExportImages() + MagicConstant.COMMA).contains(MagicConstant.COMMA + key + MagicConstant.COMMA)) {
                        ImageEntity image = new ImageEntity();
                        image.setHeight(200);
                        image.setWidth(500);
                        image.setUrl(row.get(key) + "");
                        row.put(key, image);
                    }
                }
                rows.add(row);
                //什么时候开始写?
                if ((templateModel.getExportStep() > 0 && rows.size() >= templateModel.getExportStep()) || rows.size() >= data.size()) {
                    workbook = ExcelExportUtil.exportExcel(params, entity, rows);
                    rows.clear();
                    try {
                        FileOutputStream fos = new FileOutputStream(folder + "/" + templateModel.getExportName() + " [" + i + "]" + "." + System.currentTimeMillis() + ".xls");
                        workbook.write(fos);
                    } catch (IOException e) {
                        throw new MagicException("分片流处理失败!", 8000000);
                    }
                }
            }
            //开始压缩
            try {
                ZipKit.zipFile(folder, folder + ZipKit.ZIP_SUFFIX);
            } catch (Exception e) {
                throw new MagicException("分片压缩失败!", 9000000);
            }
            log.info("导出耗时: " + (System.currentTimeMillis() - start.getTime()));
        }
        return false;
    }

    /**
     * 启动引擎的执行类
     *
     * @param idcDatasource
     * @param idcDatasourceType
     * @param resolveScript
     * @param templateModel
     * @return
     */
    public Map<String, Object> run(Map<String, Object> idcRuntimePool, IdcDatasource idcDatasource, IdcDatasourceType idcDatasourceType, String resolveScript, TagTemplate templateModel) {
        Map<String, Object> res = Maps.newHashMap();
        if (StringKit.isBlank(resolveScript)) {
            res.put(templateModel.getResultKey(), "");
            return res;
        }
        DruidDataSource dataSource = null;
        try {
            dataSource = dataSourcePool.getDataSource(idcRuntimePool, idcDatasourceType, idcDatasource);
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new MagicException("数据源切换初始化失败! (" + idcDatasourceType.getCode() + " -> " + idcDatasource.getId() + ")");
        }
        I i = M.init(dataSource);
        i.setIndex(String.valueOf(idcRuntimePool.get(RuntimeConstant.RUNTIME_ORIGN_SCRIPT_INDEX)));
        i.setResultKey(templateModel.getResultKey());
        i.setTemplateName(templateModel.getName());
        i.setIsPure((Boolean) ObjectUtils.defaultIfNull(idcRuntimePool.get(RuntimeConstant.RUNTIME_IS_PURE), Boolean.FALSE));
        i.setScript(resolveScript);
        Isv isv = (Isv) idcRuntimePool.get(RuntimeConstant.RUNTIME_ISV);
        if ("query".equals(templateModel.getMethod().toLowerCase())) {
            switch (templateModel.getResultType().toLowerCase()) {
                //查询: 多条结果集
                case "rows":
                    List<Map<String, Object>> rows = i.queryList();
                    if (1 == isv.getAppAutoCutDbPrefix() && null != rows) {
                        List<Map<String, Object>> tmpRows = Lists.newArrayList();
                        for (Map<String, Object> row : rows) {
                            row = autoCutTbPrefix(row);
                            tmpRows.add(row);
                        }
                    }
                    res.put(templateModel.getResultKey(), rows);
                    return res;
                //查询: 单条结果集
                case "row":
                    Map<String, Object> row = i.queryOne();
                    if (1 == isv.getAppAutoCutDbPrefix() && null != row) {
                        row = autoCutTbPrefix(row);
                    }
                    res.put(templateModel.getResultKey(), row);
                    return res;
                //查询: 数字型结果集
                case "count":
                    Long number = i.count();
                    res.put(templateModel.getResultKey(), number);
                    return res;
                default:
                    throw new MagicException("无法匹配的ResultType类型! (" + templateModel.getId() + " -> " + templateModel.getResultType() + ")", 59800);
            }
        } else if ("update".equals(templateModel.getMethod().toLowerCase())) {
            //更新(注意: 更新, 插入, 删除都是用这个玩意儿.)   返回影响到的数据条数.
            int num = i.updateRecord();
            res.put(templateModel.getResultKey(), num);
            return res;
        } else {
            throw new MagicException("无法匹配的Method类型! (" + templateModel.getId() + " -> " + templateModel.getMethod() + ")", 59801);
        }
    }


    /**
     * 自动去除表前缀
     *
     * @param row
     * @return
     */
    public Map<String, Object> autoCutTbPrefix(Map<String, Object> row) {
        Map<String, Object> tmpRow = Maps.newHashMap();
        Iterator<Map.Entry<String, Object>> iterator = row.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> entry = iterator.next();
            String key = entry.getKey();
            Object value = entry.getValue();
            if (key.contains(".")) {
                key = key.substring(key.indexOf(".") + 1, key.length());
            }
            tmpRow.put(key, value);
        }
        return tmpRow;
    }
}
